package com.itzdm.file.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itzdm.common.context.UserContext;
import com.itzdm.common.exception.BadRequestException;
import com.itzdm.common.utils.MinioUtils;
import com.itzdm.file.mapper.FileMapper;
import com.itzdm.file.domain.po.SysFile;
import com.itzdm.file.domain.vo.FileVO;
import com.itzdm.file.service.FileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class FileServiceImpl extends ServiceImpl<FileMapper, SysFile> implements FileService {

    private final MinioUtils minioUtils;


    @Override
    public FileVO uploadFile(MultipartFile file) {
        String md5 = calculateMD5(file);
        String fileName = file.getOriginalFilename();
        if (StringUtils.isEmpty(fileName)) {
            throw new BadRequestException("文件名不能为空");
        }
        // 根据MD5查询文件
        LambdaQueryWrapper<SysFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysFile::getMd5, md5);
        // 会有多条数据有相同的MD5值
        List<SysFile> files = list(queryWrapper);
        SysFile md5File = null;
        if (!files.isEmpty()) {
            md5File = files.get(0);
        }


        // 构建文件基本信息
        SysFile fileDb = SysFile.builder()
                .createdTime(LocalDateTime.now())
                .expireTime(LocalDateTime.now().plusDays(7))
                .name(fileName)
                .type(getFileType(fileName))
                .size(file.getSize())
                .userId(UserContext.getUser())  // 替换为 UserContext.getUser()
                .md5(md5)
                .path(md5File != null ? md5File.getPath() : minioUtils.uploadFile(file))
                .build();

        // 保存文件信息
        save(fileDb);

        // 构建返回对象
        return FileVO.builder()
                .id(fileDb.getId())
                .type(fileDb.getType())
                .path(fileDb.getPath())
                .build();
    }


    /**
     * 使用File对象上传文件
     *
     * @param file             文件对象
     * @param originalFileName 原始文件名
     * @return 文件VO
     */
    @Override
    public FileVO uploadFile(File file, String originalFileName) {
        try {
            // 创建MultipartFile对象
            FileInputStream input = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile(
                    "file",
                    originalFileName,
                    Files.probeContentType(file.toPath()),
                    input
            );
            input.close();

            // 调用现有的上传方法
            return uploadFile(multipartFile);
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }

    private Integer getFileType(String fileName) {
        int filePath;
        if (fileName.endsWith(".jpg") || fileName.endsWith(".png") || fileName.endsWith(".jpeg")) {
            filePath = 1;
        } else if (fileName.endsWith(".mp4") || fileName.endsWith(".avi") || fileName.endsWith(".mov")) {
            filePath = 3;
        } else filePath = 2;
        return filePath;
    }

    private String calculateMD5(MultipartFile file) {
        try (InputStream is = file.getInputStream()) {  // 使用try-with-resources自动关闭流
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192]; // 使用8KB的缓冲区
            int read;

            while ((read = is.read(buffer)) > 0) {
                md.update(buffer, 0, read);
            }

            byte[] md5Bytes = md.digest();

            // 将字节数组转换为十六进制字符串
            StringBuilder sb = new StringBuilder();
            for (byte b : md5Bytes) {
                sb.append(String.format("%02x", b));
            }

            return sb.toString();
        } catch (NoSuchAlgorithmException | IOException e) {
            throw new RuntimeException("计算文件MD5失败", e);
        }
    }
}